﻿namespace AtomicCms.Web
{
    using System;
    using System.Configuration;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;
    using Common.Extensions;
    using Common.Logging;
    using Core.Extensions;
    using Core.IoC;
    using Core.Mvc;
    using Core.Routes;
    using Data;
    using log4net;
    using log4net.Config;
    using log4net.Repository.Hierarchy;
    using NHibernate;
    using NHibernate.Context;

    public class MvcApplication : HttpApplication
    {
        protected void Application_Error(object sender, EventArgs e)
        {
            ILoggingService logger = IoC.Get<ILoggingService>();
            Exception ex = Server.GetLastError();
            logger.Exception(ex);
        }

        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapRoute("account",
                            "account/{action}",
                            new {controller = "Account"});
            EntryRoute entryRoute = new EntryRoute("{name}-{id}/",
                                                   new RouteValueDictionary(
                                                       new
                                                           {
                                                               controller = "Home",
                                                               action = "Content",
                                                               id = 0,
                                                               name = string.Empty
                                                           }),
                                                   new RouteValueDictionary(new {id = @"\d+"}),
                                                   new MvcRouteHandler());

            routes.Add("display-entry",
                       entryRoute);
            routes.MapRoute("infrastructure",
                            "{action}/",
                            new {controller = "Infrastructure"});
            routes.MapRoute("tag", "tag/{tag}/", new {controller = "Home", action="Tag"});
            routes.MapRoute("general", "{controller}/{action}/{id}", new {controller="Home", action="Default",id = UrlParameter.Optional});
        }


        public static void SetAdoNetAppenderConnectionStrings(string connectionStringKey)
        {
            var hier = (Hierarchy)LogManager.GetRepository();
            if (hier != null)
            {
                var appenders = hier.GetAppenders().OfType<log4net.Appender.AdoNetAppender>();

                foreach (var appender in appenders)
                {
                    appender.ConnectionString = ConfigurationManager.ConnectionStrings[connectionStringKey].ConnectionString;
                    appender.ActivateOptions();
                }
            }
        }

        protected void Application_Start()
        {
            InitializeLogging();

            SetAdoNetAppenderConnectionStrings("AtomicCms");
            StructureMapRegistration.RegisterAll();

            AreaRegistration.RegisterAllAreas();
            RegisterRoutes(RouteTable.Routes);
            ViewEngines.Engines.Clear();
            ViewEngines.Engines.Add(new SkinSupportViewEngine());
            ControllerBuilder.Current.SetControllerFactory(new AtomicCmsControllerFactory());
        }

        private static void InitializeLogging()
        {
            System.IO.FileInfo configFile = new System.IO.FileInfo(HttpContext.Current.Server.MapPath("log4net.config"));
            XmlConfigurator.ConfigureAndWatch(configFile);
        }

        protected void Application_BeginRequest(object sender, EventArgs e)
        {
            this.OpenSessionInView();
            this.SeoRedirects();
        }

        private void OpenSessionInView()
        {
            string path = Context.Request.Url.AbsolutePath;
            if (this.IgnoreExtension(path))
            {
                return;
            }

            ManagedWebSessionContext.Bind(Context,
                                          SessionManager.SessionFactory.OpenSession());
        }

        private bool IgnoreExtension(string path)
        {
            return path.EndsWith(".css",
                                 StringComparison.InvariantCultureIgnoreCase) ||
                   path.EndsWith(".js",
                                 StringComparison.InvariantCultureIgnoreCase) ||
                   path.EndsWith(".png",
                                 StringComparison.InvariantCultureIgnoreCase) ||
                   path.EndsWith(".jpg",
                                 StringComparison.InvariantCultureIgnoreCase) ||
                   path.EndsWith(".gif",
                                 StringComparison.InvariantCultureIgnoreCase);
        }

        private void CloseSessionInView()
        {
            string path = Context.Request.Url.AbsolutePath;
            if (this.IgnoreExtension(path))
            {
                return;
            }

            ISession session = ManagedWebSessionContext.Unbind(Context,
                                                               SessionManager.SessionFactory);
            if (session != null)
            {
                if (session.Transaction != null &&
                    session.Transaction.IsActive)
                {
                    session.Transaction.Rollback();
                }

                session.Close();
            }
        }

        protected void Application_EndRequest(object sender, EventArgs e)
        {
            this.CloseSessionInView();
        }

        private void SeoRedirects()
        {
            if (Request.HttpMethod.Equals("POST",
                                          StringComparison.InvariantCultureIgnoreCase))
            {
                // seo rules can be applyed only to GET requests
                return;
            }

            string absolutePath = HttpContext.Current.Request.Url.AbsolutePath;
            string query = HttpContext.Current.Request.Url.Query;

            string url = HttpContext.Current.Request.Url.ToString();


            if (this.RemoveDoubleSlashes(absolutePath))
            {
                this.PermanentRedirect(url.Replace("//",
                                                   "/") + query);
            }

            if (this.AddTrailingSlash(absolutePath))
            {
                this.PermanentRedirect(url + "/" + query);
            }
        }

        public bool RemoveDoubleSlashes(string absolutePath)
        {
            // If we have double-slashes, strip them out
            if (absolutePath.Contains("//"))
            {
                return true;
            }

            return false;
        }

        public bool RemoveWWWPrefix(string scheme,
                                    string authority,
                                    string absolutePath,
                                    string query,
                                    out string newUrl)
        {
            if (authority.StartsWith("www.",
                                     StringComparison.InvariantCultureIgnoreCase))
            {
                newUrl = (scheme + "://" + authority.Remove(0,
                                                            4) +
                          absolutePath);
                return true;
            }

            newUrl = null;
            return false;
        }

        public void PermanentRedirect(string url)
        {
            HttpResponse response = HttpContext.Current.Response;
            response.PermanentRedirect(url);
        }

        public bool AddTrailingSlash(string absolutePath)
        {
            absolutePath = absolutePath.NullSafe();
            return !absolutePath.Contains(".") && !absolutePath.EndsWith("/");
        }
    }
}